package com.xizi.lotterySystem.service.mq;

import com.xizi.lotterySystem.common.config.DirectRabbitConfig;
import com.xizi.lotterySystem.common.exception.ServiceException;
import com.xizi.lotterySystem.common.exception.errorCode.ErrorCode;
import com.xizi.lotterySystem.controller.param.DrawPrizeParam;
import com.xizi.lotterySystem.controller.param.ShowWinningRecordsParam;
import com.xizi.lotterySystem.dao.mapper.ActivityPrizeMapper;
import com.xizi.lotterySystem.service.IDrawPrizeService;
import com.xizi.lotterySystem.service.activitystatus.IActivityStatusManager;
import com.xizi.lotterySystem.dao.dataobject.ActivityPrizeDO;
import com.xizi.lotterySystem.service.dto.ConvertActivityStatusDTO;
import com.xizi.lotterySystem.dao.dataobject.WinningRecordDO;
import com.xizi.lotterySystem.service.enumerate.ActivityPrizeStatusEnum;
import com.xizi.lotterySystem.service.enumerate.ActivityStatusEnum;
import com.xizi.lotterySystem.service.enumerate.ActivityUserStatusEnum;
import com.xizi.lotterySystem.utils.EmailUtil;
import com.xizi.lotterySystem.utils.JacksonUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Component
@RabbitListener(queues = DirectRabbitConfig.QUEUE_NAME)
public class MqReceiver {

    private static final Logger log = LoggerFactory.getLogger(MqReceiver.class);


    @Autowired
    private IActivityStatusManager activityStatusManager;

    @Autowired
    private IDrawPrizeService drawPrizeService;

    @Autowired
    private ThreadPoolTaskExecutor asyncServiceExecutor;

    @Autowired
    private EmailUtil emailUtil;
    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;

    @RabbitHandler
    public void process(Map<String,String> map) throws Exception {
        String data = map.get("data");
        DrawPrizeParam param = JacksonUtil.readValue(data, DrawPrizeParam.class);
        log.info("接收到 Mq中 的消息, message:{}",JacksonUtil.writeValueAsString(param));
        List<WinningRecordDO> winningRecordDOList = null;
        //  参数校验
        drawPrizeService.checkDrawPrizeParam(param);

        try {
            // 进行状态扭转
            statusConvert(param);
            // 保存中奖者信息
            winningRecordDOList = drawPrizeService.saveWinningRecords(param);
            // 给中奖者发送邮件消息  （如:发送多种途径的中奖消息 采用 异步方式）
            // 这里只用了  qq   邮箱
            syncExecute(winningRecordDOList);
        }catch (ServiceException e){
            log.error("error:{},{}",e.getCode(),e.getMessage());
            // 回滚
            rollbackWinning(param);

            throw new ServiceException(new ErrorCode(e.getCode(),e.getMessage()));
        }catch (Exception e){
            log.error("error:{}",e.getMessage(),e);
            // 回滚
            rollbackWinning(param);
            throw new Exception();
        }
        // 结束
        log.info("process（） 方法处理Rabbit Mq 中一个消息完成！");
    }

    private void rollbackWinning(DrawPrizeParam param) {
        if(!convertStatusSuccess(param)){
            log.info("数据库中抽奖相关状态无异常，不需要回滚!");
            return;
        }
        // 1.回滚 奖品 和 中奖人 活动状态状态
        rollbackStatusConvert(param);
        // 2.删除 winning_record 表中奖人信息
        if(!hasRecords(param)){
            return;
        }

        // 删除中奖记录 + 缓存
        List<Long> winnerId = param.getWinnerList().stream()
                .map(DrawPrizeParam.Winner::getUserId)
                .collect(Collectors.toList());

        drawPrizeService.removeRecords(param.getActivityId(), param.getPrizeId(),winnerId);
    }

    private boolean hasRecords(DrawPrizeParam param) {
        ShowWinningRecordsParam showWinningRecordsParam = new ShowWinningRecordsParam();
        showWinningRecordsParam.setActivityId(param.getActivityId());
        showWinningRecordsParam.setPrizeId(param.getPrizeId());
        int count = drawPrizeService.selectWinningRecordsCount(showWinningRecordsParam);

        return count > 0;
    }


    //判断是否需要回滚
    private boolean convertStatusSuccess(DrawPrizeParam param) {
        // 1. 奖品 和 中奖人 的状态是绑定的，其中一个修改了，其他的也会修改
        // 所以我们判断 奖品的状态是否修改了，就行
        if(null == param || null == param.getActivityId() || null == param.getPrizeId()){
            log.warn("convertStatusSuccess() , 方法中参数 为空！param{}",JacksonUtil.writeValueAsString(param));
            return false;
        }

        ActivityPrizeDO activityPrizeDO = activityPrizeMapper.selectByAPID(param.getActivityId(), param.getPrizeId());
        if(activityPrizeDO.getStatus().equalsIgnoreCase(ActivityPrizeStatusEnum.COMPLETED.name())){
            return true;
        }
        return false;
    }


    private void syncExecute(List<WinningRecordDO> winningRecordDOList) {
        //发  QQ 邮件
        asyncServiceExecutor.execute(() -> sendEmail(winningRecordDOList));
    }

    private void sendEmail(List<WinningRecordDO> winningRecordDOList) {
        if(winningRecordDOList == null || winningRecordDOList.isEmpty()){
            log.warn("获奖列表 为空！不用发送邮件！");
            return;
        }

        for (WinningRecordDO wRDO : winningRecordDOList) {
            try {
                String content = "Hi，"+ wRDO.getWinnerName() + "。恭喜你在"+ wRDO.getActivityName()
                        +"活动中获得"+ wRDO.getPrizeTier() +"，奖品为"+ wRDO.getPrizeName() +"。获" +
                        "奖时间为"+ wRDO.getWinningTime() +"，请尽快领取您的奖励！" ;
                emailUtil.sendSampleMail(wRDO.getWinnerEmail(),"中奖",content);
            }catch (Exception e){
                log.error("error:{}",e.getMessage(),e);
            }
        }
    }

    private void statusConvert(DrawPrizeParam param) {
        ConvertActivityStatusDTO activityStatusDTO = new ConvertActivityStatusDTO();

        activityStatusDTO.setActivityId(param.getActivityId());
        activityStatusDTO.setTargetActivityStatus(ActivityStatusEnum.COMPLETED);
        activityStatusDTO.setActivityPrizeId(param.getPrizeId());
        activityStatusDTO.setTargetActivityPrizeStatus(ActivityPrizeStatusEnum.COMPLETED);
        activityStatusDTO.setUserIds(
                param.getWinnerList()
                        .stream()
                        .map(DrawPrizeParam.Winner::getUserId)
                        .collect(Collectors.toList())
        );
        activityStatusDTO.setTargetActivityUserStatusEnum(ActivityUserStatusEnum.COMPLETED);
        activityStatusManager.handlerEvent(activityStatusDTO);
    }

    private void rollbackStatusConvert(DrawPrizeParam param) {
        ConvertActivityStatusDTO activityStatusDTO = new ConvertActivityStatusDTO();

        activityStatusDTO.setActivityId(param.getActivityId());
        activityStatusDTO.setTargetActivityStatus(ActivityStatusEnum.RUNNING);
        activityStatusDTO.setActivityPrizeId(param.getPrizeId());
        activityStatusDTO.setTargetActivityPrizeStatus(ActivityPrizeStatusEnum.INIT);
        activityStatusDTO.setUserIds(
                param.getWinnerList()
                        .stream()
                        .map(DrawPrizeParam.Winner::getUserId)
                        .collect(Collectors.toList())
        );
        activityStatusDTO.setTargetActivityUserStatusEnum(ActivityUserStatusEnum.INIT);

        // 进行回滚
        activityStatusManager.rollbackHandlerEvent(activityStatusDTO);
    }

}
